// +----------------------------------------------------------------------
// | Manong.Cloud [ 领酷码农云 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2020 http://www.manong.cloud All rights reserved.
// +----------------------------------------------------------------------
// | Author: 稻草人 <qinuoyun@qq.com>
// +----------------------------------------------------------------------
// | Member: 围棋 飘逸者 Loumt Silence24
// +----------------------------------------------------------------------

import {isObject, isEmpty, isString, isArray} from "./utils"

import {Op} from 'sequelize';

//用于获取名称
function getTableName(name) {
  return name.replace(/\./g, '_');
}

/**
 * 设置获取参数格式
 */
function getOptions(value) {
  let $options = {};

  Object.defineProperty($options, 'order', {
    value: value['order'],
    enumerable: value['order'] ? true : false,
    writable: true,
  });

  Object.defineProperty($options, 'where', {
    value: value['where'],
    enumerable: value['where'] ? true : false,
    writable: true,
  });

  Object.defineProperty($options, 'limit', {
    value: value['limit'],
    enumerable: value['limit'] ? true : false,
    writable: true,
  });

  Object.defineProperty($options, 'offset', {
    value: value['offset'],
    enumerable: value['offset'] ? true : false,
    writable: true,
  });

  Object.defineProperty($options, 'group', {
    value: value['group'],
    enumerable: value['group'] ? true : false,
    writable: true,
  });

  Object.defineProperty($options, 'having', {
    value: value['having'],
    enumerable: value['having'].langth ? true : false,
    writable: true,
  });
  Object.defineProperty($options, 'attributes', {
    value: value['fields'],
    enumerable: value['fields'].langth ? true : false,
    writable: true,
  });

  //判断是否需要处理include
  if (value['view'] && value['view']['join']) {
    Object.defineProperty($options, 'include', {
      value: value['view']['include'],
      enumerable: value['view']['include'].langth ? true : false,
      writable: true,
    });
  }
  return $options;
}

async function getPage(value, $db) {
  let $options = {};
  //判断是否需要进行数据分页
  //这里可能存在数据阻隔问题
  if (value['page']) {
    //如果该字段存在，需要删除，避免错乱
    if (value['limit']) delete value['limit'];
    if (value['offset']) delete value['offset'];
    let {pageInfo} = value;

    //返回总的条数
    const pageObject = await $db.findAndCountAll(value);
    if (pageObject) {
      pageInfo.total = pageObject.count;
    }

    //获取分多少页
    pageInfo.count = Math.ceil(pageInfo.total / pageInfo.size);
    //设置页码和偏移量
    $options['limit'] = parseInt(pageInfo.size);
    $options['offset'] = parseInt(pageInfo.size) * parseInt(pageInfo.number - 1);
    $options['total'] = pageInfo.total || 0;
    $options['count'] = pageInfo.count || 0;
    $options['size'] = pageInfo.size || 0;
    $options['number'] = pageInfo.number || 0;
  }
  return $options;
}

function getOp(op) {
  let ops = {
    "=": Op.eq,
    "$eq": Op.eq,
    ">": Op.gt,
    "$gt": Op.gt,
    "<": Op.lt,
    "lt": Op.lt,
    ">=": Op.gte,
    "$gte": Op.gte,
    "<=": Op.lte,
    "$lte": Op.lte,
    "!=": Op.ne,
    "$in": Op.in,
    "$ne": Op.ne,
    "$is": Op.is,
    "$not": Op.not,
    "$notIn": Op.notIn,
    "$or": Op.or,
    "$like": Op.like,
    "$notLike": Op.notLike,
    "$between": Op.between,
    "$notBetween": Op.notBetween,
    "$any": Op.any
  }
  return ops[op];
}


const Table = Symbol('13e8ec840abba269af9a2dde80d8e902');

const getModel = Symbol('090c0f2ddeb33e9bc0706b0faa7de7c7');

/**
 * 轻代码
 */
class module {
  /**
   * 数据对象-为了保证安全性私有属性
   * @type {{}}
   */
  [Table] = {}

  /**
   * 应用ID
   * @type {number}
   */
  $APPID = 0

  /**
   * 处理数据查询
   * @type {[]}
   */
  options = {
    where: '',
    order: '',
    fields: '',
    view: {
      join: false,
      include: []
    },
    join: {},
    group: "",
    having: [],
    paranoid: true
  };

  /**
   * 设置前缀
   * @type {string}
   */
  prefix = ''

  /**
   * 模型代码结构
   * @param name
   * @param prefix
   * @param APPID
   * @param pattern
   * @param diagnose
   */
  constructor(name, {prefix = '', APPID = null}) {
    this.$APPID = APPID;
    this.$tableName = prefix + getTableName(name);
    this[Table] = db.getModel(this.$tableName);
    this.prefix = prefix;
  }

  /**
   * 获取模型-为了保证安全性私有方法
   */
  [getModel](name) {
    let tableName = this.prefix + getTableName(name);
    return db.getModel(tableName);
  }

  /**
   * 处理where条件问题
   * @returns {module}
   */
  where(key = '', op = '', value = '') {
    let condition = {};
    //判断是否未对象
    if (isObject(key)) {
      condition = key;
    } else if (isString(key) && isString(key) && isEmpty(value)) {
      let field = {};
      field[key] = op;
      condition = field;
    } else if (isString(key) && isString(key) && value) {
      op = [getOp(op)];
      let field = {};
      field[key] = {};
      field[key][op] = value;
      condition = field;
    } else {
      condition = key;
    }
    this.options['where'] = condition;
    return this;
  }

  /**
   * 处理排序问题
   */
  order(value, type = 'DESC') {
    if (isArray(value)) {
      this.options['order'] = value;
    } else {
      this.options['order'] = [value, type];
    }
    return this;
  }

  /**
   * 设置数据极限
   * @param  {String} limit [description]
   * @param  {String} end   [description]
   * @return {[type]}       [description]
   * { offset: 5, limit: 5 }
   */
  limit(limit = "", offset = "") {
    //设置参数信息-limit
    Object.defineProperty(this.options, 'limit', {
      value: limit,
      enumerable: limit ? true : false,
      writable: true,
    });
    //设置参数信息-offset
    Object.defineProperty(this.options, 'offset', {
      value: offset,
      enumerable: offset ? true : false,
      writable: true,
    });
    return this;
  }

  /**
   * 用于数据分页
   * @param  {String} page [description]
   * @param  {String} size [description]
   * @return {[type]}      [description]
   * page-number
   * page-size
   * page-total
   * page-count
   */
  page(page = "", size = "", detail = false) {
    this.options['page'] = true;
    this.options['pageInfo'] = {
      "number": parseInt(page),
      "size": parseInt(size),
      "detail": detail,
      "total": 0,
      "count": 0,
    }
    return this;
  }

  /**
   * 设置字段
   * @return {[type]} [description]
   */
  attr(value) {
    this.options['fields'] = value;
    return this;
  }

  /**
   * 关联leftJoin
   * @param model
   * @param foreignKey
   * @param sourceKey
   * @param attribute
   * @param where
   * @returns {module}
   */
  leftJoin(model, foreignKey, sourceKey, attribute = {}, where = {}) {
    let target = this[getModel](model);
    this.options['join'] = false;
    this.options['view'].include.push({
      where: where,
      right: false,
      model: target,
      required: false
    })
    this.options['view'].join = true;
    this[Table].hasOne(target, {foreignKey: foreignKey, sourceKey: sourceKey});
    return this;
  }

  /**
   * rightJoin
   * @param model
   * @param foreignKey
   * @param sourceKey
   * @param attribute
   * @param where
   * @returns {module}
   */
  rightJoin(model, foreignKey, sourceKey, attribute = {}, where = {}) {
    let target = this[getModel](model);
    this.options['join'] = false;
    this.options['view'].include.push({
      attribute: attribute,
      where: where,
      right: true,
      model: target,
      required: false
    })
    this.options['view'].join = true;
    this[Table].hasOne(target, {foreignKey: foreignKey, sourceKey: sourceKey});
    return this;
  }

  /**
   * innerJoin
   * @param model
   * @param foreignKey
   * @param sourceKey
   * @param attribute
   * @param where
   * @returns {module}
   */
  innerJoin(model, foreignKey, sourceKey, attribute = {}, where = {}) {
    let target = this[getModel](model);
    this.options['join'] = false;
    this.options['view'].include.push({
      where: where,
      right: false,
      model: target,
      required: true
    })
    this.options['view'].join = true;
    this[Table].hasOne(target, {foreignKey: foreignKey, sourceKey: sourceKey});
    return this;
  }

  /**
   * 获取单条数据
   */
  get() {
    let options = Object.assign({}, getOptions(this.options));

    return this[Table].findOne(options);
  }

  /**
   * 获取多条数据
   */
  async getAll() {
    let options = Object.assign({}, getOptions(this.options));
    let pageInfo = {};
    //用于判断是否需要分页
    if (this.options['page']) {
      pageInfo = await getPage(this.options, this[Table]);
      options.limit = pageInfo.limit
      options.offset = pageInfo.offset
    }

    if (this.options['page'] && this.options['pageInfo'] && this.options['pageInfo']['detail']) {
      return {
        children: await this[Table].findAll(options),
        total: pageInfo.total,
        count: pageInfo.count,
      }
    } else {
      return this[Table].findAll(options);
    }

  }

  /**
   * 添加数据
   */
  async add(data) {
    try {
      let returned = await this[Table].create(data);
      return returned.toJSON();
    } catch (error) {
      throw error;
    }
  }

  /**
   * 批量插入
   * @param  {[type]} data [description]
   * @return {[type]}      [description]
   */
  async adds(data) {
    try {
      const returned = await this[Table].bulkCreate(data);
      return returned;
    } catch (error) {
      throw error;
    }
  }

  /**
   * 更新数据
   * @param  {[type]} data [description]
   * @return {[type]}      [description]
   */
  async update(data, options = {}) {
    try {
      options.where = this.options['where'];
      const returned = await this[Table].update(data, options);
      return returned;
    } catch (error) {
      throw error;
    }
  }

  /**
   * 统计数据
   * @param data
   * @param options
   * @returns {Promise<*>}
   */
  async count(options = {}) {
    try {
      options.where = this.options['where'];
      const returned = await this[Table].count(options);
      return returned;
    } catch (error) {
      throw error;
    }
  }

  /**
   * 获取最大
   * @param data
   * @param options
   * @returns {Promise<*>}
   */
  async max(data, options = {}) {
    try {
      options.where = this.options['where'];
      const returned = await this[Table].max(data, options);
      return returned;
    } catch (error) {
      throw error;
    }
  }

  /**
   * 获取最小
   * @param data
   * @param options
   * @returns {Promise<*>}
   */
  async min(data, options = {}) {
    try {
      options.where = this.options['where'];
      const returned = await this[Table].min(data, options);
      return returned;
    } catch (error) {
      throw error;
    }
  }

  /**
   * 数据求和
   * @param data
   * @param options
   * @returns {Promise<*>}
   */
  async sum(data, options = {}) {
    try {
      options.where = this.options['where'];
      const returned = await this[Table].sum(data, options);
      return returned;
    } catch (error) {
      throw error;
    }
  }

  /**
   * 删除数据-默认软删除
   * @param  {[type]} data [description]
   * @return {[type]}      [description]
   */
  async delete(options = {}) {
    try {
      options.where = this.options['where'];
      const returned = await this[Table].destroy(options);
      return returned;
    } catch (error) {
      throw error;
    }
  }
}


export default module;
